# -*- coding: utf-8 -*-
from Axon.Component import component
import struct
import string
from numpy import *
import random
import sys
from Axon.Component import component
from Command import *
import Renderer
        
class Player(object):
    """ Object representing player """
    def __init__(self, player_id, startx, starty):
        print "WWWW", startx,starty
        self.player_id = player_id
        self.x = startx
        self.y = starty
        self.ready = False
        self.points = 0
        
class Ball(object):
    """ Object representing Gem """
    def __init__(self, id, type):
        self.id = id
        self.type = type 
        
    def __repr__(self):
        return '%s/%s' % (self.id, self.type)

class Board(object):
    DIR_HORIZONTAL = 1
    DIR_VERTICAL = 2
    DIR_BOTH = 3

    """ Object representing game board """
    def __init__(self, size, figures):
        self.size = size        # Size of board
        self.figures = figures  # ?
        self.matrix = None      # Board matrix
        self.you = None         
        self.id_counter = 0
    def generate(self,random):
        """ Generate board matrix """
                
        self.matrix = array([], dtype=(int, int))
        temp_figure_list = []
        for x in range(self.size**2):
            temp_figure_list.append(Ball(id=self.id_counter, type=random.randint(0,self.figures)))
            self.id_counter += 1
        #print temp_figure_list
        self.matrix = concatenate((self.matrix, temp_figure_list))
        
        self.matrix = self.matrix.reshape(self.size, self.size)
                
        # Remove thirds
        while(True):
            matches = self.checkMatches()
            newGems, movedColumns = self.removeGems(matches, random)
            if(len(matches) == 0):
                break
            
        print 'Matrix generated'            
        print self.matrix
            
    
    def move(self, player, command):
        """ Execute player command """
        oldx, oldy = player.x, player.y

        if command == COMMAND_MOVE_LEFT:
            player.x -= 1
            if player.x < 0:
                player.x = self.size - 2
        if command == COMMAND_MOVE_RIGHT:
            player.x += 1
            if player.x >= self.size -1:
                player.x = 0

        if command == COMMAND_MOVE_UP:
            player.y -= 1
            if player.y < 0:
                player.y = self.size - 1
        if command == COMMAND_MOVE_DOWN:
            player.y += 1
            if player.y >= self.size:
                player.y = 0            


        if abs(player.x - oldx) == 1:
            print 'Switching horizontally from %dx%d to %dx%d' % (oldx, oldy, player.x, player.y)
        elif abs(player.y - oldy) == 1:
            print 'Switching vertically from %dx%d to %dx%d' % (oldx, oldy, player.x, player.y)
        else:
            print 'going to %dx%d' % (player.x, player.y)
                
        return player.x, player.y
    
    def switch(self,player,random):
        temp = self.matrix[player.y][player.x+1]
        self.matrix[player.y][player.x+1] = self.matrix[player.y][player.x]
        self.matrix[player.y][player.x] = temp
        return self.removeMatches(random)
    
    def removeMatches(self,random):
        matches = self.checkMatches()
        newGems, movedGems = self.removeGems(matches, random)
        return matches,newGems,movedGems
        
    def removeGems(self,matches,random):
        import copy
        newGems = []
        movedColumns = []
        # Remove matched gems
        for match_dir,cords in matches:
            for x,y in cords:
                self.matrix[y][x] = None
                        
        print self.matrix
        # Generate new gems
        for x in xrange(0,self.size):
            newGems.append([])
            for y in xrange(0,self.size):
                if self.matrix[y][x] == None:
                    newGems[x].append(Ball(self.id_counter,random.randint(0,self.figures)))
                        
        # Move gems down
        newGemsWork = copy.deepcopy(newGems)
        for y in xrange(0,self.size):
            for x in xrange(0,self.size):
                if(self.matrix[y][x] != None):
                    continue
        
                print newGemsWork
                print x,y                
                print matrix

                movedColumns.append((x,y))
                for i in xrange(y,0,-1):                    
                    self.matrix[i][x] = self.matrix[i-1][x]
                                    
                self.matrix[0][x] = newGemsWork[x][0]
                newGemsWork[x].remove(newGemsWork[x][0])
                            
        return newGems,movedColumns
        
    def checkMatches(self):
        matches = []
        exclude = {}
        for x in xrange(0,self.size):
            for y in xrange(0,self.size):
                # Exclude some dir?
                dir = self.DIR_BOTH
                if (x,y) in exclude:
                    dir -= exclude[(x,y)]
                    
                actMatches = self.checkMatch(x,y,dir)
                matches += actMatches
                # Update excludes
                for match in actMatches:
                    if(len(match[1]) > 3):
                        for i in xrange(1,len(match[1])-2):
                            exclude[match[1][i]] = match[0]

        print matches
        return matches
                        
    def checkMatch(self,x,y,dir=DIR_BOTH):
        type = self.matrix[y][x].type
        nx = 1
        ny = 1
        if(dir == self.DIR_BOTH or dir == self.DIR_HORIZONTAL):
            while(x+nx < self.size and self.matrix[y][x+nx].type == type):        
                nx+=1
        if(dir == self.DIR_BOTH or dir == self.DIR_VERTICAL):                
            while(y+ny < self.size and self.matrix[y+ny][x].type == type):
                ny+=1
            
        ret = []
        if(nx >= 3):
            ret.append((self.DIR_HORIZONTAL,[ (i,y) for i in xrange(x,x+nx)]))
        if(ny >= 3):
            ret.append((self.DIR_VERTICAL,[ (x,i) for i in xrange(y,y+ny)]))
        return ret            
                
    def bind_player(self, player_id):
        self.you = player_id


class GameState(component):
    Inboxes = {  "CommandInbox" : "Incoming commands (keys) from Client or from Server - the are unified!",
                 "serverInbox"  : "Incoming info messages", }
    Outboxes = { "RenderOutbox" : "Renderer output",
                 "cmdBroadcast" : "Used on server,broadcast to clients",
                 "cmdDirect"    : "Used on server,direct comm to clients",
                 "cmdDirectInit"    : "Used on server,direct comm to clients",}    
    board = None
    seed = None

    def __init__(self, size=10, figures=4,server = False):
        super(GameState, self).__init__()        
        self.seed = str(random.random())
        self.random = random.Random()
        self.random.seed(self.seed)
        self.size = size
        self.server = server
        self.figures = figures
        self.player_dict = {}   # Players
        self.board = Board(size, figures)
                
    def new_player(self, player_id,x=-1,y=-1):
        if x == -1 or y == -1:  
            player = Player(player_id=player_id, startx=self.random.randint(0,self.size-1), starty=self.random.randint(0,self.size-1))
        else:
            player = Player(player_id=player_id, startx=x, starty=y)
        self.player_dict[player_id] = player
        return player
    
    def _createBoardGems(self):
        x = 0
        y = 0
        for line in self.board.matrix:
            x = 0            
            for gem in line: 
                self.send((Renderer.RENDER_CREATE_SPHERE, gem.type, x,y), "RenderOutbox")
                x+=1
            y+=1
            
    def calcPoints(self,matches):
        points = 0;
        for dir, cords in matches:
            points += len(cords) * 100
        return points
            
    def _generateSwitchRender(self,player,matches,newGems,movedColumns):
        if(player != None):
            self.send((Renderer.RENDER_SWAP_SPHERE, player.x,player.y,player.x+1,player.y), "RenderOutbox")
                          
        # Remove old gems
        for dir,cords in matches:
            for x,y in cords:
                self.send((Renderer.RENDER_REMOVE_SPHERE, x,y), "RenderOutbox")
                
        # Create gems 
        for x in xrange(0,self.size):
            for y in xrange(0,len(newGems[x])):
                self.send((Renderer.RENDER_CREATE_SPHERE, newGems[x][y].type,x,-1 - y), "RenderOutbox")
               
        # Move gems 
        for x,y in movedColumns:
            for i in xrange(y,0,-1):
                self.send((Renderer.RENDER_MOVE_SPHERE, x,i-1,x,i), "RenderOutbox")
            for i in xrange(0,len(newGems[x])):
                self.send((Renderer.RENDER_MOVE_SPHERE, x,-1-i,x,-i), "RenderOutbox")
            newGems[x].pop()                            
                
    def main(self):        
        while 1:
            # Process commands from users
            while self.dataReady("CommandInbox"):

                commands = self.recv("CommandInbox")
                player_id = commands[0]
                command = commands[1]
                                
                print 'GameState', commands
                
                
                if command in (COMMAND_MOVE_UP, COMMAND_MOVE_DOWN, COMMAND_MOVE_LEFT, COMMAND_MOVE_RIGHT):
                    x, y = self.board.move(self.player_dict[player_id], command)
                    self.send((Renderer.RENDER_MOVE_CURSOR, player_id, x, y), "RenderOutbox")
                    self.send(commands,"cmdBroadcast")
                
                elif command == COMMAND_SWITCH:
                    player = self.player_dict[player_id]
                    
                    
                    matches,newGems,movedGems = self.board.switch(player,self.random)
                    self._generateSwitchRender(player,matches,newGems,movedGems)
                    player.points += self.calcPoints(matches)
                    
                    while(True):
                        matches,newGems,movedGems = self.board.removeMatches(self.random)
                        if(len(matches) == 0):
                            break
                        
                        player.points += self.calcPoints(matches)
                        self._generateSwitchRender(None,matches,newGems,movedGems)
                        
                    self.send((Renderer.RENDER_SET_SCORE, player_id,player.points), "RenderOutbox")
                                                
                    #if points > 0:
                    #    pass #calculate and draw on X
                    self.send(commands,"cmdBroadcast")

                elif command == COMMAND_INIT_PLAYER:                    
                    player = self.new_player(player_id)
                    sink = commands[2]
                    self.send((player_id,sink), "cmdDirectInit")
                    
                    print player_id,player,player.x,player.y
                                                            
                    self.send((Renderer.RENDER_CREATE_CURSOR, player_id, player.x, player.y), "RenderOutbox")
                    self.send((player_id,COMMAND_CREATE_PLAYER,player.x, player.y),"cmdBroadcast")
                    
                    self.send((player_id,0,COMMAND_SET_SEED,self.seed),"cmdDirect")
                    for p_id in self.player_dict:
                        if p_id != player_id:
                            player = self.player_dict[p_id]
                            self.send((player_id,p_id,COMMAND_CREATE_PLAYER, player.x, player.y),"cmdDirect")
                        
                
                elif command == COMMAND_CREATE_PLAYER:
                    player = self.new_player(player_id,commands[2],commands[3])                    
                    self.send((Renderer.RENDER_CREATE_CURSOR, player_id, player.x, player.y), "RenderOutbox")

                elif command == COMMAND_SET_SEED:
                    self.seed = commands[2]
                    
                elif command == COMMAND_SET_READY:
                    self.player_dict[player_id].ready = True
                    self.send(commands,"cmdBroadcast")
                    gameReady = True
                    for player in self.player_dict.values():
                        if(player.ready == False):
                            gameReady = False
                            break
                    if(gameReady):
                        self.random.seed(self.seed)
                        self.board.generate(self.random)
                        self._createBoardGems()
                        print self.board.checkMatches()
                                                
                else:
                    print 'invalid command in inbox'
            
            
            
            if not self.anyReady():
                self.pause()

            yield 1


